DATA DRIVEN SECURITY

Victor Gómez Gamero

Marc Portavella Boixader


Introducción

El siguiente informe recoge un estudio de datos para la asignatura data driven security. Para ello se ha escogido la temática de threat intelligence donde se analizan datos sobre IPs maliciosas.

Con este estudio se pretende llevar a cabo una primera toma de contacto con un conjunto de datos bastante grande sobre Ips maliciosas y ver que patrones o observaciones se pueden sacar, a su vez ver posibles aplicaciones para realizar predicciones.

Recolección de los Datos

Los datos se han recogido de distintas fuentes para poder tener información complementaria:

En el github de firehol encontramos un conjunto de ficheros que contienen blacklists mantenidas por terceros. En ellas encontramos las ips maliciosas identificadas diariamente y clasificadas segun su categoria.

En la web de maxmind encontramos una base de datos gratuita con la información de geolocalización de las ips. A su vez proporcionan una api para trabajar con la base de datos.

En la web de worldbank encontramos datos demográficos a nivel mundial.

Definición de funciones

A continuación definimos las funciones utilizadas para el tratamiento de datos. Hay que tener en cuenta que la función getdataframe tiene bastante coste temporal, por ello, es preferible guardar el dataframe en disco para luego cargarlo.

import os
import glob
import pandas
import geoip2.database as db
import datetime
from plotnine import *
import matplotlib.pyplot as plt
import plotly as py
import plotly.express as px
import plotly.graph_objects as go

pandas.options.display.max_columns = 10

def getdataframe(source,dest = ""):
    files = glob.glob(os.path.join(source,"*.ipset"))
    dftotal = pandas.DataFrame(columns=["IP","Category","Maintainer","Country","Date"])
    reader = db.Reader(r"./GeoLite2-Country_20191210/GeoLite2-Country.mmdb")
    for f in files:
        ips = []
        countrylist = []
        cat = ""
        main = ""
        date = ""
        df = pandas.DataFrame(columns=["IP","Category","Maintainer","Country","Date"])
        aux = open(f,"r")
        content = aux.readlines()
        aux.close()
        for l in  content:
            if "Category" in l:
                cat = str(l.split(":")[-1]).strip()
            elif "Maintainer" in l:
                main = str(l.split(":")[-1]).strip().replace("/","")
            elif "#" not in l:
                ips.append(str(l).strip())
            elif "This File Date" in l:
                date = str(l.split(" : ")[-1]).strip().replace("  "," ")
                try:
                    date = str(datetime.datetime.strptime(date, "%a %b %d %H:%M:%S UTC %Y"))
                except:
                    print(str(l))
        df["IP"] = ips
        df["Category"] = [cat]*len(ips)
        df["Maintainer"] = [main]*len(ips)
        df["Date"] = [date]*len(ips)
        for ip in df.IP.values:
            try:
                c = reader.country(ip).country.names["en"]
            except:
                c = ''
            countrylist.append(c)
        df["Country"] = countrylist
        dftotal = dftotal.append(df, ignore_index = True)
    if dest != "" and not os.path.exists(dest):
        dftotal.to_csv(dest,index=False)
    return dftotal

def dropduplicates(dataframe,groupcolumns):
    aux = dataframe.copy()
    aux["Occurrence"] = df.groupby(groupcolumns).cumcount() + 1
    return aux.drop_duplicates(subset = groupcolumns, keep = "last")
    
def correlation(dataframe1,dataframe2):
    normalized = []
    country = []
    for c in dataframe2.columns[2:]:
        try:
            a = int(dataframe2[c])
            b = int(dataframe1.loc[dataframe1["Country Name"] == c]["2018"])
            normalized.append((a/b)*1000)
            country.append(c)
        except:
            pass
    aux = pandas.DataFrame(columns=["Country","Risk"])
    aux["Country"] = country
    aux["Risk"] = normalized
    return aux
    
def correlation2(dataframe):
    it = dataframe.iterrows()
    try:
        next(it)
        country = []
        category = []
        for i in range(len(dataframe.index)):
            aux = next(it)
            country.append(aux[0])
            a1 = aux[1].keys().to_list()
            a2 = list(aux[1].values)
            c = a1[a2.index(max(a2))]
            category.append(c)
    except:
        pass
    dfret = pandas.DataFrame(columns=["Country","Category"])
    dfret["Country"] = country
    dfret["Category"] = category
    return dfret

def correlation3(df3,df4,df6,df7):
    #Es creen llistes buides que anem omplint amb les dades que ens interesen
    normalized = []
    country = []
    code = []
    category = []
    category2 = []
    for c in df4.columns[2:]:
        try:
            a = int(df4[c])
            b = int(df3.loc[df3["Country Name"] == c]["2018"])
            normalized.append((a/b)*1000)
            country.append(c)
            #Es fa una cerca per trobar el país amb el que coincideix
            #i treure l'iterador per introduir el codi correcte
            s = -1
            for cou in df3.iloc[0:,0]:
                s = s+1
                if cou == c:
                    code.append(df3.at[s, 'Country Code'])
            it = -1
            for i in df6.iloc[0:,0]:
                it = it+1
                if i == c:
                    category.append(df6.at[it, 'Category'])
            it2 = 0
            t = False
            for i2 in df7.iloc[0:,0]:
                if i2 == c:
                    category2.append(df7.at[it2, 'Category'])
                    t = True
                it2 = it2+1
                if it2 == 225:
                    if t == False:
                        category2.append("No data")
                    t = False
        except:
            pass
    aux = pandas.DataFrame(columns=["Country","Risk", "Code", "Category","No Abuse"])
    aux["Country"] = country
    aux["Risk"] = normalized
    aux["Code"] = code
    aux["Category"] = category
    aux["No Abuse"] = category2
    return aux

Datos elegantes

Los datos obtenidos, se han parseado y limpiado para poder sacar la información relevante. De firehol se ha extraido la siguiente información : IP, Categoría, Quien mantiene la lista donde aparece dicha ip y fecha de la lista. De maxmind hemos obtenido la geolocalización de cada ip mediante su API. Hay que tener en cuenta que de los datos en bruto se podía extraer más información que no nos ha parecido relevante para este estudio. Con lo mencionado se ha generado el siguiente dataframe con toda la información parseada. La primera linea de código genera de nuevo el dataframe, con la segunda, cargamos uno preguardado.

#df = getdataframe(r".\blocklist-ipsets-master")
df = pandas.read_csv(r".\brutedataframe_country_date.csv")
df
IP Category Maintainer Country Date
0 1.0.104.86 reputation www.alienvault.com Japan 2019-12-16 10:52:34
1 1.0.130.91 reputation www.alienvault.com Thailand 2019-12-16 10:52:34
2 1.0.130.96 reputation www.alienvault.com Thailand 2019-12-16 10:52:34
3 1.0.130.127 reputation www.alienvault.com Thailand 2019-12-16 10:52:34
4 1.0.130.163 reputation www.alienvault.com Thailand 2019-12-16 10:52:34
... ... ... ... ... ...
2773363 222.186.36.58 organizations pgl.yoyo.orgadservers China 2019-12-13 13:40:03
2773364 222.187.221.28 organizations pgl.yoyo.orgadservers China 2019-12-13 13:40:03
2773365 222.236.44.131 organizations pgl.yoyo.orgadservers South Korea 2019-12-13 13:40:03
2773366 223.165.25.36 organizations pgl.yoyo.orgadservers India 2019-12-13 13:40:03
2773367 223.165.31.15 organizations pgl.yoyo.orgadservers India 2019-12-13 13:40:03

2773368 rows × 5 columns

Por último con la información de worldbank hemos generado un dataframe que contiene información relevante por país.

df1 = pandas.read_csv(r".\API_SP.POP.TOTL_DS2_en_csv_v2_566132.csv")
df1
Country Name Country Code Indicator Name Indicator Code 1960 ... 2016 2017 2018 2019 Unnamed: 64
0 Aruba ABW Population, total SP.POP.TOTL 54211.0 ... 104872.0 105366.0 105845.0 NaN NaN
1 Afghanistan AFG Population, total SP.POP.TOTL 8996973.0 ... 35383128.0 36296400.0 37172386.0 NaN NaN
2 Angola AGO Population, total SP.POP.TOTL 5454933.0 ... 28842484.0 29816748.0 30809762.0 NaN NaN
3 Albania ALB Population, total SP.POP.TOTL 1608800.0 ... 2876101.0 2873457.0 2866376.0 NaN NaN
4 Andorra AND Population, total SP.POP.TOTL 13411.0 ... 77297.0 77001.0 77006.0 NaN NaN
... ... ... ... ... ... ... ... ... ... ... ...
259 Kosovo XKX Population, total SP.POP.TOTL 947000.0 ... 1816200.0 1830700.0 1845300.0 NaN NaN
260 Yemen, Rep. YEM Population, total SP.POP.TOTL 5315355.0 ... 27168210.0 27834821.0 28498687.0 NaN NaN
261 South Africa ZAF Population, total SP.POP.TOTL 17099840.0 ... 56203654.0 57000451.0 57779622.0 NaN NaN
262 Zambia ZMB Population, total SP.POP.TOTL 3070776.0 ... 16363507.0 16853688.0 17351822.0 NaN NaN
263 Zimbabwe ZWE Population, total SP.POP.TOTL 3776681.0 ... 14030390.0 14236745.0 14439018.0 NaN NaN

264 rows × 65 columns

Estudios generados a partir del dataset de un día

Inicialmente se ha trabajado en un primer estudio solo con los datos de un solo día obtenidos de firehol. Con ello se ha querido realizar observaciones a pequeña escala antes de pasar a grandes conjuntos de datos.

Distribución de ips maliciosas por categoría

Se ha observado como se reparten las ips maliciosas en las distintas categorías definidas por firehol.

A continuación se ha transformado la información ya limpia para representar correctamente lo que se quería observar. Para ello se ha usado la información recopilada en el datafrme df. Dado que df contenía ips repetidas en la misma categoría, se han llevado a cabo distintos pasos para agrupar la información de forma conveniente.

En primer lugar, se ha agrupado toda la información del dataframe por IP/categoría y a continuación se han eliminado los duplicados.

df2 = dropduplicates(df,["IP","Category"])
df2
IP Category Maintainer Country Date Occurrence
0 1.0.104.86 reputation www.alienvault.com Japan 2019-12-16 10:52:34 1
1 1.0.130.91 reputation www.alienvault.com Thailand 2019-12-16 10:52:34 1
2 1.0.130.96 reputation www.alienvault.com Thailand 2019-12-16 10:52:34 1
3 1.0.130.127 reputation www.alienvault.com Thailand 2019-12-16 10:52:34 1
4 1.0.130.163 reputation www.alienvault.com Thailand 2019-12-16 10:52:34 1
... ... ... ... ... ... ...
2773363 222.186.36.58 organizations pgl.yoyo.orgadservers China 2019-12-13 13:40:03 1
2773364 222.187.221.28 organizations pgl.yoyo.orgadservers China 2019-12-13 13:40:03 1
2773365 222.236.44.131 organizations pgl.yoyo.orgadservers South Korea 2019-12-13 13:40:03 2
2773366 223.165.25.36 organizations pgl.yoyo.orgadservers India 2019-12-13 13:40:03 1
2773367 223.165.31.15 organizations pgl.yoyo.orgadservers India 2019-12-13 13:40:03 2

1373455 rows × 6 columns

Por último, se ha pivotado la información para quedara una tabla representado lo que se quería observar y se ha realizado una agregado por columnas.

df3 = df2.pivot(index = "IP", columns = "Category", values = "Occurrence")
df3
Category abuse anonymizers attacks malware organizations reputation spam
IP
0.0.0.1 NaN NaN NaN 1.0 NaN NaN NaN
0.14.1.6 NaN NaN NaN NaN 4.0 NaN NaN
0.15.0.1 NaN NaN NaN NaN 4.0 NaN NaN
0.15.6.5 NaN NaN NaN NaN 4.0 NaN NaN
0.15.9.1 NaN NaN NaN NaN 4.0 NaN NaN
... ... ... ... ... ... ... ...
99.99.178.63 4.0 NaN NaN NaN NaN NaN NaN
99.99.203.59 2.0 NaN NaN NaN NaN NaN NaN
99.99.253.228 NaN NaN NaN NaN 1.0 NaN NaN
99.99.38.14 1.0 NaN NaN NaN NaN NaN NaN
99.99.60.120 NaN NaN 1.0 NaN NaN NaN NaN

1306224 rows × 7 columns

a = df3.agg("sum")
a
Category
abuse            1383986.0
anonymizers       133325.0
attacks           538736.0
malware           185034.0
organizations      91017.0
reputation        236887.0
spam              204383.0
dtype: float64

Con los datos obtenidos en distintas etapas del tratamiento, podemos representar de varias formas la distribución según categoría:

ggplot(df, aes(x="Category")) + geom_bar(stat = 'count')
<ggplot: (97737331250)>
def donutplot(a):
        fig = plt.figure()
        fig.patch.set_facecolor("white")
        plt.rcParams["text.color"] = "black"
        my_circle = plt.Circle((0,0), 0.7, color="white")
        plt.pie(a.values,labels=df3.columns)
        p = plt.gcf()
        p.gca().add_artist(my_circle)
        return p
p = donutplot(a)

Indice de peligrosidad por país

Los datos obtenidos se han utilizado para observar y calcular un índice de peligrosidad por país. Para conseguir dicho índice se han usado los datos de df y df3. Para poder representar la información correctamente, se ha generado un nuevo dataframe que contiene el país con su índice calculado en base a nº IPs maliciosas / Población.

El dataframe df4 contiene la distribución de IPs por país.

df4 = dropduplicates(df,["IP"])
df4 = df4.pivot(index = "IP", columns = "Country", values = "Occurrence")
df4 = df4.agg(["sum"])
df4
NaN Afghanistan Albania Algeria American Samoa ... Vietnam Yemen Zambia Zimbabwe Åland
sum 10521.0 392.0 3205.0 6689.0 25.0 ... 105709.0 365.0 558.0 495.0 22.0

1 rows × 242 columns

Finalmente en el dataframe df5 podemos ver la correlación, esta nos proporciona un indice de riesgo Risk.

df5 = correlation(df1,df4)
df5
Country Risk
0 Albania 1.118137
1 Algeria 0.158400
2 American Samoa 0.450735
3 Andorra 3.337402
4 Angola 0.017300
... ... ...
175 Uzbekistan 0.045759
176 Vanuatu 0.116168
177 Vietnam 1.106433
178 Zambia 0.032158
179 Zimbabwe 0.034282

180 rows × 2 columns

Distribución de categorías por país

Una vez hemos encontrado este índice de peligrosidad, hemos pensado en añadir también una observación que contemplase la categoría mas repetida en cada país. Esto junto con el índice, te da más información aún para atribuir un valor de peligro a cada país, ya que no es lo mismo mantener ips categorizadas por distribución de spam que categorizadas por distribución de malware. Para ello hemos agrupado el numero de IPs de cada categoría que mantenía cada país y el más grande es el que hemos anotado.

Una vez hecho el agregado nos ha quedado el siguiente dataframe.

dfcat = df.copy()
df6 = dropduplicates(dfcat,["Category","Country"])
df6 = df6.pivot(index = "Country", columns = "Category", values = "Occurrence")
df6 = correlation2(df6)
df6
Country Category
0 Afghanistan abuse
1 Albania abuse
2 Algeria abuse
3 American Samoa abuse
4 Andorra malware
... ... ...
236 Vietnam abuse
237 Yemen abuse
238 Zambia abuse
239 Zimbabwe abuse
240 Åland abuse

241 rows × 2 columns

El problema que se puede observar es que la gran mayoría de bloques de cada país es de abuse por lo tanto, viendo que se repetía mucho, hemos optado a hacer la lista sin contar esta categoría de cara a mostrar otros datos relevantes como es el segundo bloque mas mantenido.

dfcat = df.copy()
dfcat = df[df.Category != "abuse"]
df7 = dropduplicates(dfcat,["Category","Country"])
df7 = df7.pivot(index = "Country", columns = "Category", values = "Occurrence")
df7 = correlation2(df7)
df7
Country Category
0 Afghanistan attacks
1 Albania anonymizers
2 Algeria spam
3 American Samoa anonymizers
4 Andorra malware
... ... ...
220 Vietnam spam
221 Yemen anonymizers
222 Zambia attacks
223 Zimbabwe attacks
224 Åland anonymizers

225 rows × 2 columns

Agrupación y ordenación

Una vez obtenidos todos los datos estructurados en distintos bloques, se ha procedido a coordinar los diversos dataframes. Mayormente, el problema que hemos tenido es que cada dataframe ponía los países de una forma diferente o ponía más o menos países por lo que ha sido necesario realizar una reestructuración y normalización de la información.

Así que la solución ha sido hacer un script para buscar las coincidencias y así escribir un dataframe final con esto. evitando así tener que construir nosotros unos dataframes idénticos durante las distintas observaciones y sólo tener que invertir tiempo para la construcción del dataframe final para mostrarlo gráficamente.

Normalización para el mapa

A continuación, como se ha comentado anteriormente se muestra la normalización realizada en los nombres de los países para poder trabajar con los distintos dataframes:

def rewrite(dataframe):
        d = {'Venezuela, RB':'Venezuela',"Egypt, Arab Rep.":"Egypt","Korea, Rep.":"South Korea","Iran, Islamic Rep.":"Iran","Yemen, Rep.":"Yemen",
        "Czech Republic":"Czechia","Syrian Arab Republic":"Syria","Congo, Rep.":"Congo Republic",
        "Slovak Republic":"Slovakia","Lithuania":"Republic of Lithuania","Moldova":"Republic of Moldova",
        "Kyrgyz Republic":"Kyrgyzstan","Lao PDR":"Laos","Jordan":"Hashemite Kingdom of Jordan","Congo, Dem. Rep.":"DR Congo",
        "Cote d'Ivoire":"Ivory Coast","Seychelles":"Sey","British Virgin Islands":"BR","Russian Federation":"Russia"}
        dataframe = dataframe.replace(d)
        dataframe.drop(dataframe.columns[len(dataframe.columns)-1],axis=1,inplace=True)
        return dataframe

El último problema que hemos afrontado ya de cara a normalizar los colores del mapa y adelantandonos un poco a la conclusión, es que algunos de los países que tienen bastantes bloques maliciosos no estaban representados en ningun mapa de cloropeth debido a ser paraísos fiscales muy pequeños y negligibles como puede ser Seychelles o Virgin Islands, así que los hemos tenido que retirar por la falta de datos.

Dataframe final

Finalmente, una vez agrupado todo en un dataframe, hemos aplicado esta información para construir un modelo de mapa de cara a mostrar visualmente la cantidad de bloques maliciosos de IPs por población.

El resultado es el siguiente dataframe:

dataframe final

df1rw = rewrite(df1)
df8 = correlation3(df1rw,df4,df6,df7)
df8
Country Risk Code Category No Abuse
0 Albania 1.118137 ALB abuse anonymizers
1 Algeria 0.158400 DZA abuse spam
2 American Samoa 0.450735 ASM abuse anonymizers
3 Andorra 3.337402 AND malware malware
4 Angola 0.017300 AGO abuse spam
... ... ... ... ... ...
190 Venezuela 0.212780 VEN abuse attacks
191 Vietnam 1.106433 VNM abuse spam
192 Yemen 0.012808 YEM abuse anonymizers
193 Zambia 0.032158 ZMB abuse attacks
194 Zimbabwe 0.034282 ZWE abuse attacks

195 rows × 5 columns

Mapa

El mapa esta hecho con la librería plotly. A partir de los códigos que hemos añadido en los dataframes y en base al dataframe final, se ha generado un mapa interactivo que nos permite ver como de peligroso es un país.

def map():
        dfmap = df8.copy()
        dfmap['text'] = dfmap['Country'] + '<br>' +         dfmap['Code'] + '<br>' +         'Main category: '  + dfmap['Category'] + '<br>' +         'Category withouth abuse: ' + dfmap['No Abuse']
        fig = go.Figure(data=go.Choropleth(
            locations = dfmap['Code'],
            z = dfmap['Risk'],
            text = dfmap['text'],
            colorscale = 'Reds',
            autocolorscale=False,
            reversescale=False,
            marker_line_color='darkgray',
            marker_line_width=0.5,
            colorbar_tickprefix = '',
            colorbar_title = 'number of risk',
        ))
        fig.update_layout(
            title_text='Daily Global Risk',
            geo=dict(
                showframe=False,
                showcoastlines=False,
                projection_type='equirectangular'
            ),
            annotations = [dict(
                x=0.55,
                y=0.1,
                xref='paper',
                yref='paper',
                text='Source: Data Driven Class',
                showarrow = False
            )]
        )
        py.offline.plot(fig,filename="mapa.html")
map()

Conclusiones

El resultado que podemos observar al final es bastante obvio con respecto al mapa. Los países que son paraísos fiscales como Belize, Seychelles o Virgin Islands por ejemplo, mantienen un número muy elevado de bloques maliciosos en relación a su población.

Seguidamente el segundo grupo con más bloques maliciosos es el soviético, y también se puede remarcar Islandia y Holanda que se unen a este bloque.

Se puede intuir que la causa es que estos países al tener una legislación más laxa, son una oportunidad para la gente que le interese mantener bloques de IPs que pueden servir para atacar al resto de países ya que así evitaran controles y repercusiones legales.

Estudios generados a partir del Dataset de dos meses

Una vez terminado el trabajo que la información que nos proporcionaba un día, se ha querido atacar mas datos para entrar en el concepto de las evoluciones temporales.

El estudio que hemos generado a partir del dataset de dos meses es algo diferente. En este caso, partiendo de las IPs de dos meses, lo que hemos hecho es coger todos los días en activo que ha tenido cada IP reportada como maliciosa y agruparlo por país de cara a ver que países podían acumular mas días con bloques maliciosos reportados y confirmados. Con ello podemos ver que mantenimiento se da en cada país a este tipo de IPs.

Los datos sobre los que se ha trabajado son los proporcionados en la asignatura:

df = pandas.read_csv(r".\df2month.csv", encoding = "ISO-8859-1")
df
date ioc category descr mnt
0 2019-10-15 02:48:08 1.0.69.58 reputation IP reputation database Alien Vault
1 2019-10-15 02:48:08 1.0.76.41 reputation IP reputation database Alien Vault
2 2019-10-15 02:48:08 1.0.84.116 reputation IP reputation database Alien Vault
3 2019-10-15 02:48:08 1.0.116.165 reputation IP reputation database Alien Vault
4 2019-10-15 02:48:08 1.0.118.213 reputation IP reputation database Alien Vault
... ... ... ... ... ...
2734866 2019-10-11 21:08:04 222.186.36.58 organizations IPs of ad servers Yoyo.org
2734867 2019-10-11 21:08:04 222.187.221.28 organizations IPs of ad servers Yoyo.org
2734868 2019-10-11 21:08:04 222.236.44.131 organizations IPs of ad servers Yoyo.org
2734869 2019-10-11 21:08:04 223.165.25.36 organizations IPs of ad servers Yoyo.org
2734870 2019-10-11 21:08:04 223.165.31.15 organizations IPs of ad servers Yoyo.org

2734871 rows × 5 columns

Distribución por días

Concretamente en este caso hemos utilizado 4 funciones y hemos generado 4 dataframes distintos hasta llegar al final que hemos utilizado para generar este último mapa.

Primeramente hemos hecho el agregado de cuantos días ha estado una IP activa en estos dos meses

def days_repited(df):
  #Para sacar dias en total que una IP que se ha tenido en activo en dos meses
  count_series = df.groupby(['ioc']).size()
  return count_series.to_frame(name = 'Dias').reset_index()

Esto nos permite ver cuantos días se ha repetido esta IP como podemos ver en esta tabla:

df9 = days_repited(df)
df9
ioc Dias
0 0.0.0.1 1
1 0.0.10.40 3
2 0.0.10.42 3
3 0.0.10.44 3
4 0.0.10.45 3
... ... ...
1339746 99.99.139.67 3
1339747 99.99.203.59 4
1339748 99.99.253.228 1
1339749 99.99.38.14 2
1339750 99.99.60.120 1

1339751 rows × 2 columns

Distribución por países

Seguidamente hemos pensado que podíamos analizar de estos datos así que hemos decidido agrupar estas IPs por países de cara a consultar si los bloques reportados maliciosos solían aguantar mucho o no.

Para ello primero hemos tenido que averiguar el país de cada IP, así que hemos usado la siguiente función.

def country(df1,dest = ""):
  # Sacamos el pais de cada IP maliciosa
  df = pandas.DataFrame(columns=["IP","Days","Country"])
  dftotal = pandas.DataFrame(columns=["IP","Days","Country"])
  reader = db.Reader(r"./GeoLite2-Country_20191210/GeoLite2-Country.mmdb")
  ips = []
  countrylist = []
  days = []
  s = 0
  for ip in df1.iloc[0:,0]:
    try:
      c = reader.country(ip).country.names["en"]
      countrylist.append(c)
      ips.append(df1.at[s, 'ioc'])
      days.append(df1.at[s, 'Dias'].astype(int))
    except:
      #c = ''
      pass
    s = s +1
  df["IP"] = ips
  df["days"] = days
  df["Country"] = countrylist
  dftotal = dftotal.append(df, ignore_index = True)
  if dest != "" and not os.path.exists(dest):
    dftotal.to_csv(dest,index=False)
  return dftotal

Con el país, hemos hecho el agregado para ver cuantos días acumulaban en total a partir de la siguiente función.

def agg_country(df):
  # Agrupamos en total cuantos dias acumula cada pais de IPs maliciosas. Para saber si
  # ese pais por ejemplo elimina rapido sus IPs reportadas como maliciosas
  count_series = df.groupby(['Country']).size()
  new_df = count_series.to_frame(name = 'Dias totales').reset_index()
  return new_df

El resultado es bastante interesante ya que se podía ver mucha diferencia en la acumulación por país

El dataframe generado es el siguiente:

#df10 = country(df9,dest= r"./IP_Days_total.csv")
df10 = pandas.read_csv(r"./IP_Days_total.csv")
df10 = agg_country(df10)
df10
Country Dias totales
0 Afghanistan 225
1 Albania 1365
2 Algeria 4870
3 American Samoa 7
4 Andorra 111
... ... ...
239 Wallis and Futuna 1
240 Yemen 185
241 Zambia 420
242 Zimbabwe 420
243 Åland 9

244 rows × 2 columns

Normalización para el uso del mapa y dataframe final

Hemos tenido que generar otra vez el código para el uso del mapa con la siguiente función

def code(df,df5):
  # Para meter el codigo ya que sino el mapa no se genera.
  s = -1
  country = []
  days = []
  code = []
  s1 = -1
  for c in df.iloc[0:,0]:
    s = -1
    s1 = s1 +1
    try:
      for cou in df5.iloc[0:,0]:
        s = s+1
        if cou == c:
          #print (df5.at[s, 'Country Code'],df5.at[s, 'Country Name'])
          code.append(df5.at[s, 'Country Code'])
          country.append(df5.at[s, 'Country Name'])
          days.append(df.at[s1, 'Dias totales'])
    except:
      pass
  aux = pandas.DataFrame(columns=["Country","Dias totales", "Code"])
  aux["Country"] = country
  aux["Dias totales"] = days
  aux["Code"] = code
  #El dataframe final usado para el mapa es este
  return aux

El dataframe final usado para el mapa es el siguiente:

df11 = code(df10,df1rw)
df11
Country Dias totales Code
0 Afghanistan 225 AFG
1 Albania 1365 ALB
2 Algeria 4870 DZA
3 American Samoa 7 ASM
4 Andorra 111 AND
... ... ... ...
192 Venezuela 3590 VEN
193 Vietnam 72668 VNM
194 Yemen 185 YEM
195 Zambia 420 ZMB
196 Zimbabwe 420 ZWE

197 rows × 3 columns

Segundo mapa

Este mapa nos muestra el numero total de días acumulados por cada país de sus IPs maliciosas reportadas en estos dos meses del dataframe.

def map2():
        dfmap = df11.copy()
        dfmap['text'] = dfmap['Country'] + '<br>' +         dfmap['Code']
        fig2 = go.Figure(data=go.Choropleth(
            locations = dfmap['Code'],
            z = dfmap['Dias totales'],
            text = dfmap['text'],
            colorscale = 'Blues',
            autocolorscale=False,
            reversescale=False,
            marker_line_color='darkgray',
            marker_line_width=0.5,
            colorbar_tickprefix = '',
            colorbar_title = 'Dias totales',
        ))
        fig2.update_layout(
            title_text='Total de dias que han estado las IPs reportadas como maliciosas activas en dos meses',
            geo=dict(
                showframe=False,
                showcoastlines=False,
                projection_type='equirectangular'
            ),
            annotations = [dict(
                x=0.55,
                y=0.1,
                xref='paper',
                yref='paper',
                text='Source: Data Driven Class',
                showarrow = False
            )]
        )
        py.offline.plot(fig2,filename="mapa22.html")
map2()
050k100k150k200kDias totalesTotal de dias que han estado las IPs reportadas como maliciosas activas en dos mesesSource: Data Driven Class

Conclusiones

Las conclusiones que podemos sacar de este mapa son bastante curiosas. Principalmente podemos observar claramente que el país que más días en activo mantiene bloques maliciosos de IPs es Uruguay. Uruguay precisamente no es uno de los países más problemáticos en este aspecto pero parece ser que tiene una política de eliminación de contenido malicioso en Internet bastante mala.

Otro conjunto interesante es el de Indonesia donde parece que tampoco hay mucha legislación para retirar contenido malicioso o si la hay no se realizan controles para su aplicación.

Podemos observar donde en España y en general en el resto de Europa, los bloques maliciosos apenas duran pocos días. Rusia o China a pesar de ser unos gigantes en cuanto a ataques maliciosos parece ser que tampoco mantienen en activo por mucho tiempo sus IPs problemáticas.

Trabajo futuro

Este estudio de datos se podría usar para definir parámetros para intentar predecir la posibilidad que una IP se vaya a incluir en blacklist y en que categoría se incluiría.

Datos relevantes para Predecir

Para poder entrenar un modelo de predicción hay que disponer de suficientes datos con atributos releventes diferenciados. De este modo y en base a los atributos se puede realizar la predicción.

  • País de origen
  • Veces en blacklist días distintos
  • Veces en blacklist mismo día
  • Tiempo en uso (mismo propietario)
  • Categoría en blacklist
  • Tiempo en blacklist